package com.pnnd.stream;

import com.pnnd.utils.CollectionUtils;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * 管理高维数组的缓冲区，缓冲区由若干个内存段组成
 * @see MemorySegment
 * 内存段可能是不连续的，但是数据是连续的，inputStream来完成连续和不连续的内存映射
 * @see NdArrayInputStream
 */
public abstract class AbstractBuffer implements MemoryReaderWriter,Buffer,MemorySegmentContainer {

    private int free;
    protected int start;
    protected int offset;
    protected int limit;
    private BufferType type;
    protected List<MemorySegment> segments;
    private boolean available;
    private static BufferView view;

    private MemorySegment currentSegment;

    private BufferTag tag;
    private boolean recycled;

    protected abstract byte[] getByteAddress();

    public abstract InputStream getInputStream();
    public abstract OutputStream getOutputStream();

    public long getFree() {
        return this.free;
    }

    @Override
    public boolean isCache() {
        return false;
    }

    /**
     * 内存是否连续
     * @return
     */
    public abstract boolean isContinuous();

    /**
     * buffer 已写入数据的偏移地址
     * offset 永远是相对于0的
     * @return long
     */
    @Override
    public int offset() {
        return this.offset;
    }

    @Override
    public BufferTag tag() {
        return this.tag;
    }

    @Override
    public BufferType type() {
        return this.type;
    }

    @Override
    public void tagAsEvent() {

    }

    @Override
    public Collection<MemorySegment> getMemorySegment() {
        return CollectionUtils.readonly(this.segments);
    }

    @Override
    public MemorySegment currentMemorySegment() {
        return this.currentSegment;
    }

    @Override
    public int getMemorySegmentOffset() {
        return 0;
    }

    @Override
    public void recycleBuffer() {
        for(MemorySegment segment:this.segments){
            segment.free();
        }

        this.segments = new ArrayList<>();
    }

    @Override
    public boolean isRecycled() {
        return this.recycled;
    }

    @Override
    public Buffer retainBuffer() {
        return null;
    }

    @Override
    public Buffer readOnlySlice() {
        return new ReadOnlyBuffer(this.segments,this.start,this.limit);
    }

    @Override
    public Buffer readOnlySlice(int index, int length) {

        if(this.start+index > length){
            throw new IndexOutOfBoundsException();
        }

        ReadOnlyBuffer buffer = new ReadOnlyBuffer(this.segments,this.start+index,length);



        return buffer;
    }

    @Override
    public int getMaxCapacity() {
        return this.limit - this.start;
    }

    @Override
    public int getReaderIndex() {
        return 0;
    }

    @Override
    public void setReaderIndex(int readerIndex) throws IndexOutOfBoundsException {

    }

    @Override
    public int getSize() {
        return 0;
    }

    @Override
    public void setSize(int writerIndex) {

    }

    @Override
    public int readableBytes() {
        return this.offset - this.start;
    }

    @Override
    public ByteBuffer getNioBufferReadable() {

        if(this.isContinuous()){
            byte[] bs = this.getByteAddress();

            int offset = (int)this.segments.get(0).start() + this.start;

            return ByteBuffer.wrap(bs,offset,this.limit - this.start);
        }

        throw new RuntimeException();
    }

    public BufferView getView(){

        if(view!=null){
            return view;
        }

        int len = 0;

        for(MemorySegment segment:this.segments){
            len += segment.size();
        }

        view = new BufferView(this.segments,len,0);

        return view;
    }

    @Override
    public ByteBuffer getNioBuffer(int index, int length) throws IndexOutOfBoundsException {
        if(this.isContinuous()){
            byte[] bs = this.getByteAddress();

            int offset = (int)this.segments.get(0).start() + this.start + index;

            if(this.limit - this.start - index < length){
                throw new IndexOutOfBoundsException();
            }

            return ByteBuffer.wrap(bs,offset,length);
        }

        throw new RuntimeException();
    }

    @Override
    public void extend(MemorySegment segment) {
        if(this.tag().extendable()){
            this.segments.add(segment);
        }
    }

    @Override
    public void dump(String path) {

    }

    @Override
    public int capacity(){
        if(segments == null){
            return 0;
        }

        return this.segments.stream().
                map(s->s.size()).reduce((a,b)->a+b).
                orElseThrow(RuntimeException::new);
    }

    /**
     * @see BufferType
     */
    public BufferType getType() {
        return type;
    }

    /**
     * 当buffer类型不是socket时有效，如果buffer类型是socket则返回null
     * @return MemorySegment
     */
    public List<MemorySegment> getSegments() {
        return CollectionUtils.readonly(this.segments);
    }

    public void recovery(){
        if(segments == null){
            return;
        }

        for(MemorySegment segment:segments){
            segment.free();
        }
    }

    /**
     * 是否可用
     * @return
     */
    public boolean isAvailable() {
        return available;
    }

    public static void main(String[] args){
        byte[] bs = new byte[100];

        for(int i=0;i<100;i++){
            bs[i] = (byte)i;
        }

        ByteBuffer byteBuffer = ByteBuffer.wrap(bs,10,30);

        byte[] b = byteBuffer.array();

        System.out.println(b == bs);

        for(Byte s:b){
            System.out.println(s);
        }
    }
}
